﻿using Game.Framework;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Text))]
public class MyOutline : BaseMeshEffect
{
    public Shader outlineShader;
    private Material shaderMat;

    [Header("描边颜色")]
    [SerializeField]
    private Color outlineColor = Color.white;
    public Color OutlineColor
    {
        get
        {
            return outlineColor;
        }
        set
        {
            outlineColor = value;
            if (base.graphic != null)
                _Refresh();
        }
    }

    [Header("描边宽度"), Range(0, 8)]
    [SerializeField]
    private float outlineWidth = 0;
    public float OutlineWidth
    {
        get
        {
            return outlineWidth;
        }
        set
        {
            outlineWidth = value;
            if (base.graphic != null)
                _Refresh();
        }
    }

#if UNITY_EDITOR
    protected override void OnValidate()
    {
        base.OnValidate();
        if (CheckShader())
        {
            this.SetShaderParams();
            this._Refresh();
        }
    }
#endif

    protected override void Awake()
    {
        shaderMat = new Material(outlineShader);
        this.graphic.material = shaderMat;
    }

    protected override void OnTransformParentChanged()
    {
        if (CheckShader())
        {
            this.SetShaderChannels();
            this.SetShaderParams();
            this._Refresh();
        }
    }

    protected override void OnEnable()
    {
        base.OnEnable();
        if (CheckShader())
        {
            this.SetShaderChannels();
            this.SetShaderParams();
            this._Refresh();
        }
    }

    bool CheckShader()
    {
        if (base.graphic == null)
        {
            LogManager.LogError("No Graphic Component !");
            return false;
        }
        if (base.graphic.material == null)
        {
            LogManager.LogError("No Material !");
            return false;
        }

        return true;
    }

    void SetShaderParams()
    {
        if (shaderMat != null)
        {
            shaderMat.SetColor("_OutlineColor", OutlineColor);
            shaderMat.SetFloat("_OutlineWidth", OutlineWidth);
        }

    }

    void SetShaderChannels()
    {
        if (base.graphic.canvas)
        {
            var v1 = base.graphic.canvas.additionalShaderChannels;
            var v2 = AdditionalCanvasShaderChannels.TexCoord1;
            base.graphic.canvas.additionalShaderChannels |= v2;
        }
    }

    public override void ModifyMesh(VertexHelper vh)
    {
        var lVetexList = new List<UIVertex>();
        vh.GetUIVertexStream(lVetexList);
        this._ProcessVertices(lVetexList, this.OutlineWidth);
        vh.Clear();
        vh.AddUIVertexTriangleStream(lVetexList);

        lVetexList.Clear();
    }


    // �����ߺ�Ϊ��ֹ��߱�����߿���У���Ҫ������������ͬʱ����UV����
    private void _ProcessVertices(List<UIVertex> lVerts, float outlineWidth)
    {
        for (int i = 0, count = lVerts.Count - 3; i <= count; i += 3)
        {
            var v1 = lVerts[i];
            var v2 = lVerts[i + 1];
            var v3 = lVerts[i + 2];
            // ����ԭ�����������ĵ�
            //
            var minX = _Min(v1.position.x, v2.position.x, v3.position.x);
            var minY = _Min(v1.position.y, v2.position.y, v3.position.y);
            var maxX = _Max(v1.position.x, v2.position.x, v3.position.x);
            var maxY = _Max(v1.position.y, v2.position.y, v3.position.y);
            var posCenter = new Vector2(minX + maxX, minY + maxY) * 0.5f;
            // ����ԭʼ���������UV�ķ���
            //
            Vector2 triX, triY, uvX, uvY;
            Vector2 pos1 = v1.position;
            Vector2 pos2 = v2.position;
            Vector2 pos3 = v3.position;
            if (Mathf.Abs(Vector2.Dot((pos2 - pos1).normalized, Vector2.right))
                > Mathf.Abs(Vector2.Dot((pos3 - pos2).normalized, Vector2.right)))
            {
                triX = pos2 - pos1;
                triY = pos3 - pos2;
                uvX = v2.uv0 - v1.uv0;
                uvY = v3.uv0 - v2.uv0;
            }
            else
            {
                triX = pos3 - pos2;
                triY = pos2 - pos1;
                uvX = v3.uv0 - v2.uv0;
                uvY = v2.uv0 - v1.uv0;
            }
            // ����ԭʼUV��
            var uvMin = _Min(v1.uv0, v2.uv0, v3.uv0);
            var uvMax = _Max(v1.uv0, v2.uv0, v3.uv0);

            // Ϊÿ�����������µ�Position��UV��������ԭʼUV��
            v1 = _SetNewPosAndUV(v1, outlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
            v2 = _SetNewPosAndUV(v2, outlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
            v3 = _SetNewPosAndUV(v3, outlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);

            // Ӧ�����ú��UIVertex
            //
            lVerts[i] = v1;
            lVerts[i + 1] = v2;
            lVerts[i + 2] = v3;
        }
    }


    private static UIVertex _SetNewPosAndUV(UIVertex pVertex, float pOutLineWidth,
        Vector2 pPosCenter,
        Vector2 pTriangleX, Vector2 pTriangleY,
        Vector2 pUVX, Vector2 pUVY,
        Vector2 pUVOriginMin, Vector2 pUVOriginMax)
    {
        // Position
        var pos = pVertex.position;
        var posXOffset = pos.x > pPosCenter.x ? pOutLineWidth : -pOutLineWidth;
        var posYOffset = pos.y > pPosCenter.y ? pOutLineWidth : -pOutLineWidth;
        pos.x += posXOffset;
        pos.y += posYOffset;
        pVertex.position = pos;
        // UV
        var uv = pVertex.uv0;
        var uvOffsetX = pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1);
        var uvOffsetY = pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1);
        uv.x += (uvOffsetX.x + uvOffsetY.x);
        uv.y += (uvOffsetX.y + uvOffsetY.y);
        pVertex.uv0 = uv;

        pVertex.uv1 = new Vector4(pUVOriginMin.x, pUVOriginMin.y, pUVOriginMax.x, pUVOriginMax.y);
        return pVertex;
    }

    private void _Refresh()
    {
        SetShaderParams();
        base.graphic.SetVerticesDirty();
    }

    private static float _Min(float pA, float pB, float pC)
    {
        return Mathf.Min(Mathf.Min(pA, pB), pC);
    }


    private static float _Max(float pA, float pB, float pC)
    {
        return Mathf.Max(Mathf.Max(pA, pB), pC);
    }


    private static Vector2 _Min(Vector2 pA, Vector2 pB, Vector2 pC)
    {
        return new Vector2(_Min(pA.x, pB.x, pC.x), _Min(pA.y, pB.y, pC.y));
    }


    private static Vector2 _Max(Vector2 pA, Vector2 pB, Vector2 pC)
    {
        return new Vector2(_Max(pA.x, pB.x, pC.x), _Max(pA.y, pB.y, pC.y));
    }
}

